package fybug.nulll.pdstream.WR.Parsing.Source;
import com.sun.istack.internal.NotNull;
import com.sun.istack.internal.Nullable;

import org.jetbrains.annotations.Contract;

import java.lang.ref.SoftReference;

import fybug.nulll.pdfunctionlibrary.Annotations.MaybeStop;
import fybug.nulll.pdfunctionlibrary.Processing.Check;
import fybug.nulll.pdfunctionlibrary.Processing.ConsistentField;
import fybug.nulll.pdfunctionlibrary.Processing.Err.Stop;
import fybug.nulll.pdfunctionlibrary.lang.Interface.Cleanable;
import fybug.nulll.pdstream.WR.Err.CharStreamErr;
import fybug.nulll.pdstream.WR.Parsing.Source.Partition.ParsingPartition;
/**
 * <h2>文本解析源超类.</h2>
 * <pre>
 * 构建 <b>解析源</b> 时必须要有一个 <b>数据源</b> 和
 * {@link ParsingPartition} 用于将数据分割出 <b>可解析的部分</b>
 * 读取出来的数据将会是符合 {@link ParsingPartition} 的 <b>分割标准</b> 的
 * 如果读取了过多的数据将会进行 <b>数据回滚</b>
 * </pre>
 *
 * @author fybug
 * @version 0.1.3
 * @see Cleanable
 * @see ParsingPartition
 * @see ConsistentField
 * @since JDK 1.8
 */
@SuppressWarnings( "All" )
abstract
class ParsingAbstractSource extends ConsistentField implements Cleanable {
    /**
     * <p>读取缓冲区</p>
     * <pre>
     * 因为该对象是可丢失的
     * 所以使用软引用
     * </pre>
     */
    @Nullable private SoftReference<StringBuilder> stringBuffer = new SoftReference<>(
            new StringBuilder());
    // 分隔符检查指针
    private volatile int mark = 0;

    @Override
    public
    int hashCode()
    { return (getClass().hashCode() + super.hashCode()) << 5; }

    /*
     * Partition
     */

    /**
     * <p>获取分割器.</p>
     *
     * @return 该解析源中的 <b>分割器</b>
     */
    @NotNull
    @MaybeStop
    public abstract
    ParsingPartition getPartition();

    /** <p>绑定分割器.</p> */
    @NotNull
    public abstract
    ParsingAbstractSource binPartition(@Nullable final ParsingPartition p);

    /*
     * Buffer
     */

    /**
     * <p>获取并检查缓冲区.</p>
     * <pre>
     * 会检查缓冲区是否被回收，如被回收
     * 将创建新的缓冲区
     * </pre>
     */
    @NotNull
    @MaybeStop // 被关闭
    protected final
    StringBuilder buffer() {
        @Nullable StringBuilder buffer;
        synchronized ( this ){
            @NotNull SoftReference<StringBuilder> softReference = Check.checkNull(stringBuffer);
            buffer = softReference.get();
            if (buffer == null)
                // 被释放
                stringBuffer = new SoftReference<>(buffer = new StringBuilder());
        }
        return buffer;
    }

    /*
     * 处理
     */

    /**
     * <p>回滚数据.</p>
     * <pre>
     * 鉴于可能不止一个 <b>分隔符</b>
     * 可能需要进行 <b>数据回滚</b>
     * </pre>
     *
     * @param size 要回滚的字符数
     */
    protected abstract
    void rollback(final int size, @NotNull final StringBuilder stringBuffer);

    /**
     * <p>从内部源读取一个字符.</p>
     *
     * @return 读取完成返回 {@code -1}
     *
     * @throws CharStreamErr 读取单个字符出错
     */
    @MaybeStop
    protected abstract
    int get() throws CharStreamErr;

    /**
     * <p>读取可处理数据.</p>
     * <p>从源读取数据,并交给{@link ParsingPartition} 进行检查后返回给 <b>解析器</b></p>
     *
     * @return 根据 <b>分割器</b> 分割出来的数据
     *
     * @throws CharStreamErr {@link #get()}
     */
    @NotNull
    public
    String read() throws CharStreamErr {
        // 检查变量
        @NotNull StringBuilder stringBuffer;
        try {
            stringBuffer = buffer(); // 获取弱引用
        } catch ( Stop e ) {
            return "";
        }
        @NotNull ParsingPartition partition; // 分割器
        try {
            partition = getPartition();
        } catch ( Stop e ) {
            // 默认分割器
            partition = new ParsingPartition(-1);
        }
        @NotNull int pook; // 当前读取
        int size; // 长度限制
        int max; // 最大缓存
        int lenght;
        try {
            synchronized ( this ){
                // 重置读取缓存和指针
                mark = 0;
                stringBuffer.delete(0, stringBuffer.length());
                // 获取数据
                size = partition.size();
                max = partition.lenght();
                while( true ){
                    /* 获取数据到结束或获取到标识符 */
                    lenght = stringBuffer.length();
                    if (size != -1 && lenght == size // 检查长度
                        || (pook = get()) == -1 // 是否读取完成
                    ) { /* 达到限制 */
                        // 当前位置到结尾的数据都传去检查
                        checkIdentifier(partition.check(stringBuffer.substring(this.mark, lenght)),
                                        stringBuffer);
                        break;
                    }
                    stringBuffer.append((char) pook);
                    lenght++;
                    if (max != -1 // 是否有标识符
                        && lenght >= mark // 检查缓存长度是否达到标识符长度
                    ) { /* 达到标识符长度 */
                        if (checkIdentifier(partition.check(
                                stringBuffer.substring(this.mark, this.mark + max - 1)),
                                            stringBuffer))
                            break;
                        this.mark++;
                    }
                }
            }
        } catch ( Stop ignored ) {
        }
        return stringBuffer.toString();
    }

    // 检查分割器的加密数据，返回是否可以抛出数据
    @Contract( pure = true )
    private
    boolean checkIdentifier(long g, @NotNull final StringBuilder stringBuffer) {
        int start,// 标识符位置
                lenght; // 标识符长度
        if (g != -1) {
            /* 找到标识符 */
            start = (int) g & 0x7fffffff;
            lenght = (int) ((g & 0xffffffff00000000L) >> 32);
            g = 0;
            rollback(stringBuffer.length() - mark - start - lenght, stringBuffer);
            // 删除多余部分和标识符
            stringBuffer.delete(this.mark + start, stringBuffer.length());
            trimSize(stringBuffer);
            return true;
        }
        trimSize(stringBuffer);
        return false;
    }

    // 当前标记次数
    private int trSize = 0;

    // 每十次 trim 一回
    private synchronized
    void trimSize(@NotNull final StringBuilder stringBuffer) {
        if (trSize >= 10) {
            stringBuffer.trimToSize();
            trSize = 0;
        } else
            trSize++;
    }

    /*
     * Check
     */

    /** <p>检查是否可以继续进行读取.</p> */
    public abstract
    boolean readly();

    /*
     * 释放
     */

    /**
     * <p>清空该解析源中的引用.</p>
     * <p>仅清空该类中的缓冲区和位置记录</p>
     */
    @Override
    public
    void clean() {
        synchronized ( this ){
            if (stringBuffer != null)
                stringBuffer.clear();
            mark = 0;
        }
    }

    /** @see #clean() */
    @Override
    public
    void free() { clean(); }

    /**
     * <p>彻底关闭该读取源.</p>
     * <pre>
     * 清除该解析器中的所有数据
     * 清除后不可再复用
     * </pre>
     */
    @Override
    public
    void close() {
        clean();
        synchronized ( this ){
            stringBuffer = null;
        }
    }
}
